﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;

namespace Doodleberg
{
    class Ball
    {
        public Vector2 velocity
        {
            get
            {
                return body.LinearVelocity * Global.farseer_conversion;
            }
            set
            {
                body.LinearVelocity = value / Global.farseer_conversion;
            }
        }

        public Vector2 position
        {
            get
            {
                return body.Position * Global.farseer_conversion;
            }
            set
            {
                body.Position = value / Global.farseer_conversion;
            }
        }

        public Vector2 startPosition;

        public Body body;

        private bool frozen;
        public float radius { get; private set; }

        public Line lastline;

        public Ball(Vector2 pos, float rad)
        {
            body = BodyFactory.CreateCircle(Global.level.phyWorld, rad / Global.farseer_conversion, 1.0f);
            body.BodyType = BodyType.Dynamic;
            position = pos;
        }


        public void reset()
        {
            position = startPosition;
            velocity = Vector2.Zero;
            unfreeze();
        }


        public void update(GameTime gameTime)
        {
            //Determine which gravity to use
            Vector2 usegravity = Vector2.Zero;
            bool isSet = false;
            foreach (GravityBox gbox in Global.level.gravityBoxes)
            {
                if (gbox.box.Contains((int)position.X, (int)position.Y))
                {
                    isSet = true;
                    usegravity += gbox.gravity;
                }
            }
            if (!isSet)
                usegravity = Global.gravity;

            Global.level.phyWorld.Gravity = usegravity / 70;

            //Check victory
            if (Global.level.goal.contains(position))
            {
                Global.gameProperties.victory = true;
                frozen = true;
            }


            if (frozen)
                body.Awake = false;
            else
                body.Awake = true;
        }


        public void collide(Line tline, GameTime gameTime)
        {
            if (tline.getDistanceTo(position.X, position.Y) < radius)
            {
                //Move back out of collision (IMPROVE)
                moveBack((float)gameTime.ElapsedGameTime.TotalSeconds);

                //If component of vector into line is small enough, roll
                //otherwise bounce
                Vector2 parallel = new Vector2();
                Vector2 perpendicular = new Vector2();

                double sS = Vector2.Dot(velocity, tline.getDirectionalVector());
                double vB = tline.getDirectionalVector().Length();

                double sLenParallel = sS / vB;

                parallel.X = (float)(sLenParallel * Math.Cos(tline.getAngle()));
                parallel.Y = (float)(sLenParallel * Math.Sin(tline.getAngle()));

                perpendicular = velocity - parallel;

                if (Math.Abs(perpendicular.Length()) > Global.rollThreshold
                    || ((position.X < tline.getVert(1).X || position.X > tline.getVert(2).X))
                    )
                {
                    //Bounce
                    Vector2 normalVect = Vector2.Zero;
                    if (position.X < tline.getVert(1).X && (
                        (tline.getVert(1).Y < tline.getVert(2).Y && position.Y < tline.getVert(1).Y)
                        || (tline.getVert(1).Y >= tline.getVert(2).Y && position.Y > tline.getVert(1).Y))
                        )
                    {
                        normalVect = Vector2.Normalize(new Vector2(position.X - tline.getVert(1).X, position.Y - tline.getVert(1).Y));
                    }
                    else if (position.X > tline.getVert(2).X && (
                        (tline.getVert(2).Y < tline.getVert(1).Y && position.Y < tline.getVert(2).Y)
                        || (tline.getVert(2).Y >= tline.getVert(1).Y && position.Y > tline.getVert(2).Y))
                        )
                    {
                        normalVect = Vector2.Normalize(new Vector2(position.X - tline.getVert(2).X, position.Y - tline.getVert(2).Y));
                    }
                    else
                    {
                        normalVect = tline.getNormalVector();
                    }

                    bounce(normalVect, tline.restitution);
                }
                else
                {
                    //Roll
                    velocity = parallel;

                    //Record
                    lastline = tline;
                }
            }
        }

        private void bounce(Vector2 normalVect, float restitution)
        {
            velocity = Vector2.Multiply(Vector2.Reflect(velocity, normalVect), restitution);
        }

        private void bounce(Ball bball)
        {
            Vector2 normalVect = Vector2.Normalize(new Vector2(position.X - bball.position.X, position.Y - bball.position.Y));
            velocity = Vector2.Multiply(Vector2.Reflect(velocity, normalVect), 0.7f);
        }

        private void accelerate(Vector2 accel, float gameTime)
        {
            velocity += accel * gameTime;
        }

        private void move(float gameTime)
        {
            position += velocity * gameTime;
        }

        private void moveBack(float gameTime)
        {
            position -= velocity * gameTime;
        }


        public void unfreeze()
        {
            frozen = false;
        }


        public void draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(
                Global.level.tex_ball,
                body.Position * Global.farseer_conversion,
                null,
                Color.White,
                0.0f,
                new Vector2(Global.level.tex_ball.Width, Global.level.tex_ball.Height) / 2,
                new Vector2(1.0f, 1.0f),
                SpriteEffects.None,
                0.8f);
        }
    }
}
